package com.piece.admin.controller.grant;

import com.piece.admin.api.dto.SysUserDTO;
import com.piece.admin.support.Route;
import com.piece.admin.event.SystemOperatorEvent;
import com.piece.admin.event.SystemPostEvent;
import com.piece.admin.model.TbSysMenu;
import com.piece.admin.model.TbSysUser;
import com.piece.admin.service.TbSysMenuService;
import com.piece.admin.service.TbSysUserService;
import com.piece.admin.support.UserInfoSupport;
import com.piece.core.framework.annotation.permission.AclPermission;
import com.piece.core.framework.constant.*;
import com.piece.core.framework.enums.OptionType;
import com.piece.core.framework.enums.StatusType;
import com.piece.core.framework.support.dto.UserDetailDTO;
import com.piece.core.framework.util.security.coder.Base64Coder;
import com.piece.core.framework.constant.SecurityConstants;
import com.piece.core.oauth.social.AuthSocialUserDTO;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.support.response.AjaxResponse;
import com.piece.core.framework.util.basic.I18nUtil;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.framework.util.security.SecurityUtil;
import com.piece.core.framework.util.security.coder.AESCoder;
import com.piece.core.framework.util.security.coder.MD5Coder;
import com.piece.core.framework.properties.ApplicationProperties;
import com.piece.core.verifycode.enums.ValidateCodeType;
import com.piece.core.verifycode.exception.ValidateCodeException;
import com.piece.core.verifycode.processor.ValidateCodeProcessorHolder;
import com.piece.core.verifycode.util.ValidateCodeUtil;
import com.piece.core.web.authority.AuthorityFactory;
import com.piece.core.web.properties.SecurityProperties;
import com.piece.member.model.UmsMember;
import com.piece.member.service.UmsMemberService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.savedrequest.DefaultSavedRequest;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Controller
@Api(tags = {"登录授权管理"})
@SessionAttributes({"authorizationRequest"})
public class GrantController {

    private RequestCache requestCache = new HttpSessionRequestCache();
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @Value("${spring.application.id:#{null}}")
    private String client_id;

    @Value("${spring.application.secret:#{null}}")
    private String client_secret;

    @Autowired
    private ApplicationProperties applicationProperties;

    @Autowired
    private SecurityProperties securityProperties;

    @Autowired
    private ValidateCodeProcessorHolder validateCodeProcessorHolder;

    @Resource
    private AuthorityFactory authorityFactory;

    @Resource
    private TbSysUserService tbSysUserService;

    @Resource
    private UmsMemberService umsMemberService;

    @Resource
    private TbSysMenuService tbSysMenuService;

    @Resource
    private SystemOperatorEvent systemOperatorEvent;

    public HttpServletRequest getRequest() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        return request;
    }

    @ApiOperation(value = "登录页面")
    @GetMapping("/authentication/login")
    public ModelAndView getLoginPage(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws IOException {
        DefaultSavedRequest savedRequest = (DefaultSavedRequest) requestCache.getRequest(request, response);

        if (null != savedRequest && savedRequest.getRequestURI().indexOf("oauth/authorize") > -1) {
            String query = savedRequest.getQueryString();
            redirectStrategy.sendRedirect(request, response, "/authentication/grant" + "?" + query);
            return null;
        }

        String authorization = "Basic " + Base64Coder.encode(client_id + ":" + client_secret);
        model.put("authorization", authorization);
        model.put("needValidate", needValidateCode(client_id));
        model.put("redirect_uri", request.getParameter("redirect_uri"));

        return new ModelAndView("/login", model);
    }

    @ApiOperation(value = "登录授权页面")
    @GetMapping("/authentication/grant")
    public ModelAndView getGrantPage(HttpServletRequest request, HttpServletResponse response, Map<String, Object> model) {
        boolean needValidate = true;

        String clientId = request.getParameter(ParamConstants.CLIENT_ID);
        String clientSecret = request.getParameter(ParamConstants.CLIENT_SECRET);
        if (StringUtil.isNotEmpty(clientId) && StringUtil.isNotEmpty(clientSecret)) {
            needValidate = needValidateCode(clientId);
            String authorization = "Basic " + Base64Coder.encode(clientId + ":" + clientSecret);
            model.put("authorization", authorization);
        }

        model.put("client_id", clientId);
        model.put("client_secret", clientSecret);
        model.put("needValidate", needValidate);
        return new ModelAndView("/grant", model);
    }

    @ApiOperation(value = "注册页面")
    @GetMapping("/authentication/register")
    public ModelAndView getSignPage(Map<String, Object> model) {
        String authorization = "Basic " + Base64Coder.encode(client_id + ":" + client_secret);
        model.put("authorization", authorization);
        model.put("needValidate", needValidateCode(client_id));
        return new ModelAndView("/register", model);
    }

    @ResponseBody
    @ApiOperation(value = "注册处理")
    @PostMapping("/authentication/doRegister")
    public AjaxResponse doRegister(@RequestBody AuthSocialUserDTO social) throws Exception {
        if (needValidateCode(client_id)) {
            ValidateCodeType type = ValidateCodeUtil.getValidateCodeType(SecurityConstants.USERNAME_CODE);
            String codeSession = getRequest().getParameter(ParamConstants.VALIDATE_CODE_SESSION);
            String validCode = getRequest().getParameter(ParamConstants.VALIDATE_CODE_CAPTCHA);
            if (StringUtil.isEmpty(codeSession)) {
                throw new ValidateCodeException(I18nUtil.message(ExceptionAuthConstants.VALIDATE_CODE_EXPIRED));
            }
            validateCodeProcessorHolder.findValidateCodeProcessor(type).validate(codeSession, validCode);
        }

        String detail = getRequest().getParameter(ParamConstants.AUTHENTICATION_INFO);
        if (null == detail) {
            detail = "";
        }
        detail = AESCoder.aesDecrypt(detail.trim());
        String loginName = StringUtil.split(detail, FrameWorkConstants.SPLIT_PARAM)[0];
        String password = StringUtil.split(detail, FrameWorkConstants.SPLIT_PARAM)[1];

        TbSysUser user = tbSysUserService.findByLoginName(loginName);
        if (null != user) {
            return AjaxResponse.error(I18nUtil.message("view.user.exist"));
        }

        user = new TbSysUser();
        String salt = SecurityUtil.randomSalt();
        password = MD5Coder.decode(loginName + password + salt);
        user.setLoginName(loginName);
        user.setUserName(social.getNickname());
        user.setGender(social.getGender());
        user.setEmail(social.getEmail());
        user.setSalt(salt);
        user.setPassword(password);

        user.setUserType(OptionType.OPTION.getCode());
        user.setStatus(StatusType.NORMAL.getCode());
        user.setCreateTime(new Date());

        Map<String, String> param = new HashMap<>();
        // 启用邮件验证
        if (Convert.toBool(applicationProperties.getValue(ApplicationConstants.REGISTER_EMAIL_ENABLE), false)) {
            user.setStatus(StatusType.DELETE.getCode());
            user = tbSysUserService.insertWithPostRole(user);
            tbSysUserService.flush();
            // 发送注册验证通知
            systemOperatorEvent.onRegisterEvent(user, SystemPostEvent.ACTION_REGISTER_VALIDATE);
            return AjaxResponse.success(I18nUtil.message("view.register.confirm"), param);
        } else {
            user = tbSysUserService.insertWithPostRole(user);
            tbSysUserService.flush();
            // 发送注册成功通知
            systemOperatorEvent.onRegisterEvent(user, SystemPostEvent.ACTION_REGISTER_SUCCESS);
            return AjaxResponse.success(I18nUtil.message(ExceptionConstants.MESSAGE_SUCCESS), param);
        }
    }

    @ApiOperation(value = "注册确认页面")
    @GetMapping("/authentication/doRegister")
    public ModelAndView getRegisterConfirmPage() {
        return new ModelAndView("/registerConfirm");
    }

    @ResponseBody
    @PostMapping("/authentication/logout/{userId}")
    @AclPermission(code = "system:user:add")
    @ApiOperation(value = "强制用户退出登录")
    @ApiImplicitParam(name = "userId", value = "用户ID", type = "path")
    public AjaxResponse forceLogout(@PathVariable String userId) {
        TbSysUser user = tbSysUserService.findById(Convert.toLong(userId));
        if (null != user) {
            boolean flag = authorityFactory.get().offLineUser(client_id, user.getLoginName());
            if (flag) {
                return AjaxResponse.success(I18nUtil.message(ExceptionConstants.MESSAGE_SUCCESS));
            }
        }
        return AjaxResponse.error(I18nUtil.message(ExceptionConstants.MESSAGE_ERROR));
    }

    @ResponseBody
    @GetMapping("/api/login/info")
    @ApiOperation(value = "获取登录用户信息")
    public AjaxResponse getUserInfo(UserDetailDTO userDetail) {
        SysUserDTO sysUser = (SysUserDTO) userDetail.getDetail();
        TbSysUser user = tbSysUserService.findById(sysUser.getUserId());
        UmsMember member = umsMemberService.findByUser(user.getUserId());
        Map<String, Object> model = new HashMap<>();
        model.put("user", user);
        model.put("member", member);
        model.put("permissions", UserInfoSupport.getPermission(user.getUserId()));
        model.put("tagsView", !Convert.toBool(applicationProperties.getValue(ApplicationConstants.APPLICATION_SINGLE_PAGE), true));
        return AjaxResponse.success(I18nUtil.message(ExceptionConstants.MESSAGE_SUCCESS), model);
    }

    @ResponseBody
    @GetMapping("/api/login/menu")
    @ApiOperation(value = "获取菜单路由")
    public AjaxResponse getUserMenus(UserDetailDTO userDetail) {
        SysUserDTO user = (SysUserDTO) userDetail.getDetail();
        List<TbSysMenu> menus = tbSysMenuService.findByUser(user.getUserId());
        menus = tbSysMenuService.findChildMenus(menus, 0);
        List<Route> routes = new Route().convert(menus);
        return AjaxResponse.success(I18nUtil.message(ExceptionConstants.MESSAGE_SUCCESS), routes);
    }

    private boolean needValidateCode(String clientId) {
        String ignore = applicationProperties.getValue(ApplicationConstants.CLIENT_IGNORE_VALIDATE);
        if (null != ignore) {
            if (StringUtil.getIndexOfString(clientId, Convert.toStrArray(ignore)) != -1) {
                return false;
            }
        }

        return true;
    }
}
